package edu.gatech.cs2340.bytebankers.presenter;

import java.util.ArrayList;

import java.util.List;
import java.util.Queue;
import java.util.Random;

import java.util.LinkedList;

import javax.swing.JLabel;
import javax.swing.JOptionPane;

import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBaseIterators.ParentIterator;
import com.sun.xml.internal.bind.v2.runtime.Location;

import edu.gatech.cs2340.bytebankers.config.InitialStartUpValues;
import edu.gatech.cs2340.bytebankers.model.Journey;
import edu.gatech.cs2340.bytebankers.model.Player;
import edu.gatech.cs2340.bytebankers.model.Trail;

import edu.gatech.cs2340.bytebankers.model.Wagon;
import edu.gatech.cs2340.bytebankers.model.item.Item;
import edu.gatech.cs2340.bytebankers.model.item.ItemManager;
import edu.gatech.cs2340.bytebankers.model.item.Ox;
import edu.gatech.cs2340.bytebankers.model.item.WagonAxle;
import edu.gatech.cs2340.bytebankers.model.item.WagonTongue;
import edu.gatech.cs2340.bytebankers.model.item.WagonWheel;
import edu.gatech.cs2340.bytebankers.view.IJourneyView;
import edu.gatech.cs2340.bytebankers.model.item.Food;
import edu.gatech.cs2340.bytebankers.notifications.GameEvents;
import edu.gatech.cs2340.bytebankers.notifications.Notifier;
import edu.gatech.cs2340.bytebankers.model.*;

public class JourneyPresenter extends Notifier implements IJourneyPresenter {

    private static final double MAX_PLAYER_HEALTH_CHANGE = 0.5;
	
    private Journey journey;
    private IJourneyView view;
    private int pace;
    private int rations;
    private Wagon wagon;
    private Trail trail;
    boolean hasBeenShown = false;
    private LinkedList<edu.gatech.cs2340.bytebankers.model.Location> locationList;
    private int turnCount;
    private Queue<Player> illList;
    private Random rand = new Random();

    /**
     * This method constructs a new JourneyPresenter
     * 
     * @param journey
     *            - the data model
     */
    public JourneyPresenter(Journey journey) {

	this.journey = journey;
	wagon = journey.getWagon();
	trail = journey.getTrail();
	locationList = trail.getLocationList();
	turnCount = 0;
	setIllList(new LinkedList<Player>());
	

    }

    /**
     * This method will send a notification to open Menu.
     */
    public void onOptionClick() {

	this.sendNotification(GameEvents.MENU_OPENED);

    }

    /**
     * This methods job is to update the model and the view.
     */
    public void onTurnClick() {
    	Boolean random = checkForRandom();
    	//turnCount++;
    	if (journey.getPace() > 2){
    		turnCount++;
    	}
    	else{
    		turnCount = 0;
    	}
    	if (random == true){
    		executeRandom();
    	}
    	updateJourney();
    	updateView();

    }

    /**
     * THis method updates the by changing the date, distance traveled, and food
     * left
     */
    private void updateView() {

    	// NOTe: Implement some day far, far away from now. Ha,Ha,Ha
    	// view.setDate(day, month, year)
    	view.setDistanceTravelled(journey.getDistanceTraveled());
    	view.setFoodLeft(wagon.getItemQuantityRemaining(journey
    			.getItemManager().getItemByType(Food.class)));
    	view.setDistanceBetweenLocations(journey.getlastLocation().getDistNext());
    	view.setPercentageTraveled((double)journey.getDistanceFromLastLocation()/journey.getlastLocation().getDistNext());
    	JLabel imageJLabel = view.getImage();
    	//System.out.println("PercentTraveled = " + view.getPercentageTraveled());
    	imageJLabel.setBounds((int)(IJourneyView.startx * (1 - view.getPercentageTraveled())), 0, 3000, 500);
    	view.setImage(imageJLabel);
    }

    /**
     * Method use to update the journey(model)
     */
    private void updateJourney() {
    	
    	// determine actual distance travelled based on:
    	// oxen health and pace and random factor
    	
    	int oxenHealth = journey.getWagon().getOxenHealth();
    	double oxenHealthPercentage = (double)oxenHealth / Wagon.MAX_OXEN_HEALTH;
    	
    	int pace = journey.getPace();    	
    	Random random = new Random();
    	
    	// get a dampener between .8 and 1.2
    	double randomizer = (double)(random.nextInt(5) + 8) / 10.0;
    	
    	int actualDistanceTraveled = (int) (pace * oxenHealthPercentage * randomizer);
    	if (actualDistanceTraveled < 1) {
			actualDistanceTraveled = 1;
		}
    	
    	//adjust player health
    	for (Player player : journey.getPlayers()) {
    		double healthChange = -(double)MAX_PLAYER_HEALTH_CHANGE * (pace / 10.0);

    		if(healthChange == 0) {
    			healthChange = MAX_PLAYER_HEALTH_CHANGE;
    		}
			player.setHealth(player.getHealth() + healthChange);
		}
    	
    	//adjust oxen health
    	

    	int distanceTraveledFromLast =journey.getDistanceFromLastLocation();
    	int distanceFromLastToNext = journey.getlastLocation().getDistNext();
    	if (distanceFromLastToNext - distanceTraveledFromLast <= journey.getPace()){
    		journey.setDistanceTraveled(journey.getDistanceTraveled() + (distanceFromLastToNext - distanceTraveledFromLast));
    		// Research how to do date arithmetic for future slices
    		// journey.setDate(journey.getDate() - 1);
    		if(wagon.useItem(journey.getItemManager().getItemByType(Food.class),
    				journey.getPlayers().size() * journey.getRationRate()
    					* InitialStartUpValues.WEIGHT_OF_A_MEAL) == -1){
    			if(!hasBeenShown){
    				JOptionPane.showMessageDialog(view.getMainPanel(), "No food left in wagon!");
    			}
    			hasBeenShown = true;
    		}
    		journey.setLastLocation(journey.getlastLocation().getNextLocation());
    		journey.setDistanceFromLastLocation(0);	//You just reached the store so this value should be zero.
    		this.sendNotification(GameEvents.LOCATION_REACHED);
    		if(journey.getlastLocation() == null) {
    			this.sendNotification(GameEvents.WINNING);		
    		}

    	}
    	else{
    		journey.setDistanceTraveled(journey.getDistanceTraveled() + actualDistanceTraveled);

			// Research how to do date arithmetic for future slices
			// journey.setDate(journey.getDate() - 1);
    		if(wagon.useItem(journey.getItemManager().getItemByType(Food.class),
    				journey.getPlayers().size() * journey.getRationRate()
    					* InitialStartUpValues.WEIGHT_OF_A_MEAL) == -1){
    			if(!hasBeenShown){
    				JOptionPane.showMessageDialog(view.getMainPanel(), "No food left in wagon!");
    			}
    			hasBeenShown = true;
    		}
			journey.setDistanceFromLastLocation(journey.getDistanceFromLastLocation() + actualDistanceTraveled);
    	}
    }

    @Override
    public void setView(IJourneyView view) {
	this.view = view;
	updateView();
    }
    
    public boolean checkForRandom(){
		Random gen = new Random();
		int currRandom = gen.nextInt(10);
		if(currRandom == 0){
			return true;
		}
		else return false;	
	}
    
    public void executeRandom(){
    	Random gen = new Random();
    	int eventNum = gen.nextInt(8);
    	switch (eventNum){
    		case 0:
    			//if good display PLAYER is now ill
    			//if ill display PLAYER is dead
    			randomIllPlayer();
    			break;
    		case 1:
    			//UGA student steals ITEM from wagon
    			randomStealItem();
    			break;
    		case 2:
    			//First Nation Hunting Party gives QUANTITY food
    			randomRecieveFood();
    			break;
    		case 3:
    			//An Ox has died
    			randomKillOx();
    			break;
    		case 4:
    			//Traveled 10 days at a pace at or above moderate. 
    			//Your oxen are tired.
    			randomTiredOx();
    			break;
    		case 5:
    			//There were severe storms. You lose DAYS of food.
    			randomSevereStorms();
    			break;
    		case 6:
    			//PLAYER has recovered from illness
    			// if no one is ill display nothing
    			randomRecoverPerson();
    			break;
    		case 7:
    			//Your wagon has be damaged. ITEM has been used to replace 
    			//the damaged part
    			randomDamagedWagon();
    			break;
    		default: break;
    	}
    	
    	
    }
    
    /**
     * Method that sets a random player to either ill or dead. If the player is initially good,
     * then the player is now ill. If the player is initially ill, the player is now dead.
     */
    public void randomIllPlayer(){
		Random gen = new Random();
		ArrayList<Player> list = (ArrayList<Player>) journey.getPlayers();
		int playerNum = gen.nextInt(list.size());
		if (list.get(playerNum).getSickStatus().compareTo("ill") == 0){
			showRandomEvenMsg(list.get(playerNum).getName() + " has died.");
			journey.killPlayer(list.get(playerNum));
		}
		else if (list.get(playerNum).getSickStatus().compareTo("good") == 0){
			journey.setIllPlayer(list.get(playerNum), "ill");
			getIllList().add(list.get(playerNum));
			showRandomEvenMsg(list.get(playerNum).getName() + " is ill.");
		}			
    }
    
    /**
     * Display the pop up notification.
     * 
     * @param msg
     */
    private void showRandomEvenMsg(String msg){
    	JOptionPane.showMessageDialog(view.getMainPanel(), msg);
    }
    
    /**
     * Removes 10 percent of a random item.
     */
    public void randomStealItem(){
    	Random gen = new Random();
    	List<Item> itemList = journey.getItemManager().getAll();
    	int loseQuant;
    	int ranNum = gen.nextInt(7);
    	Item currItem = itemList.get(ranNum);	
    	if (currItem.getName().compareTo("Box of Ammo") == 0 ||
    		currItem.getName().compareTo("Clothing") == 0	||
    		currItem.getName().compareTo("Food") == 0)
    	{
    		loseQuant = (int) (wagon.getItemQuantityRemaining(currItem) * .1);
    		if(loseQuant == 0){
    			loseQuant++;
    		}
    		wagon.useItem(currItem, loseQuant);
    		if(currItem.getName().compareTo("Food") == 0){
    			showRandomEvenMsg(loseQuant + "lbs of food have been stolen.");
    		}
    		showRandomEvenMsg(loseQuant + " " +currItem.getName() + "(s) have been stolen.");
    	}
    	else{
    		wagon.useItem(currItem, 1);
    		showRandomEvenMsg(1 + " " +currItem.getName() + " has been stolen.");
    	}
    }
    
    /**
     * Adds a random amount of food to wagon. Amount is 10-100.
     */
    public void randomRecieveFood(){
    	Random gen = new Random();
    	Item food =journey.getItemManager().getItemByType(Food.class);
    	int foodQuant = (gen.nextInt(91) +10);
    	wagon.addItemQuantity(food, foodQuant);
    	showRandomEvenMsg("First Nation Hunting Party gives " + foodQuant + " food");
    }
    
    /**
     * Method that removes an ox. If no ox remains, the game is over.
     */
    public void randomKillOx(){
    	Item ox =journey.getItemManager().getItemByType(Ox.class);
    	wagon.useItem(ox, 1);
    	showRandomEvenMsg("You lost an ox.");
    	if(wagon.getItemQuantityRemaining(ox) == 0){
    		this.sendNotification(GameEvents.LOSING);
    	}
    }
    
    /**
     * Method that sets the ox's status to ill and reduces the ox's pace by 1-3 miles.
     */
    public void randomTiredOx(){
    	if (turnCount == 10){
    		Ox ox =(Ox) journey.getItemManager().getItemByType(Ox.class);
    		ox.setSickStatus("ill");
    		//subtract 1-3 miles per day
    		int currPace = journey.getPace();
    		int deltaPace = rand.nextInt(3) + 1;
    		journey.setPace(currPace - deltaPace);
    	}
    	showRandomEvenMsg("Your oxen are tired.");
    }
    
    /**
     * Method that consumes 1-3 days worth of food.
     */
    public void randomSevereStorms(){
    	Random gen = new Random();
    	int multiple = gen.nextInt(3) + 1;
    	int reduceNum = journey.getPlayers().size() * journey.getRationRate();
    	wagon.useItem(journey.getItemManager().getItemByType(Food.class), multiple*reduceNum);
    	showRandomEvenMsg("Thunderstorm warning in your area. You had to wait it out for " + multiple + " days.");
    }
    
    /**
     * Method that kills a player.
     */
    public void randomRecoverPerson(){
    	Player currPlayer = new Player();
    	if (!getIllList().isEmpty()){
    		currPlayer = getIllList().remove();
    		showRandomEvenMsg(currPlayer.getName() + " has died.");
    		journey.killPlayer(currPlayer);
    	}    	   	
    }
    
    /**
     * Method that randomly removes either an axle, wheel, or tongue. If there is no item
     * of that type remaining, the game is over.
     */
    public void randomDamagedWagon(){
    	Random gen = new Random();
    	ArrayList<Item> array = new ArrayList<Item>();
    	array.add(journey.getItemManager().getItemByType(WagonAxle.class));
    	array.add(journey.getItemManager().getItemByType(WagonWheel.class));
    	array.add(journey.getItemManager().getItemByType(WagonTongue.class));
    	int index = gen.nextInt(3);
    	Item currItem = array.get(index);
    	if (wagon.getItemQuantityRemaining(currItem) == 0){
    		showRandomEvenMsg("You do not have enough " + currItem.getName() + "s to continue.");
    		this.sendNotification(GameEvents.LOSING);
    	}
    	else{
    		wagon.useItem(currItem, 1);
    		showRandomEvenMsg("Your wagon has been damaged and you lost a " + currItem.getName());
    	}
    }

	public Queue<Player> getIllList() {
		return illList;
	}

	public void setIllList(Queue<Player> illList) {
		this.illList = illList;
	}
}
